﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using umbraco.cms.businesslogic.web;
using System.Text;
using umbraco.cms.businesslogic.propertytype;
using System.Transactions;
using System.Data.SqlClient;
using umbraco;

namespace TheOutfield.UmbExt.DocTypeExtensions.Dialogs
{
    public partial class SwapMasterDocType : System.Web.UI.Page
    {
        public DocumentType DocType { get; set; }
        public DocumentType MasterDocType { get; set; }
        public DocumentType TargetMasterDocType { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            // TODO: Add option for mapping deleted properties to the source doc type
            var docTypeId = Convert.ToInt32("0" + Request.QueryString["id"]);
            if (docTypeId > 0)
            {
                DocType = new DocumentType(docTypeId);
                if (DocType != null)
                {
                    var masterDocTypeId = DocType.MasterContentType;
                    if (masterDocTypeId > 0)
                    {
                        MasterDocType = new DocumentType(masterDocTypeId);
                    }
                }
            }

            var targetMasterDocTypeId = Convert.ToInt32("0" + ddlTargetDocType.SelectedValue);
            if (targetMasterDocTypeId > 0)
            {
                TargetMasterDocType = new DocumentType(targetMasterDocTypeId);
            }

            if (!IsPostBack)
            {
                InitView();
            }
        }

        protected void mvSwitchMasterDocType_ActiveViewChanged(object sender, EventArgs e)
        {
            var activeView = mvSwitchMasterDocType.GetActiveView();

            if (activeView == vwSelectTargetDocType)
                BindSelectTargetDocTypeView();

            if (activeView == vwMapDocTypeProperties)
                BindMapDocTypePropertiesView();

            if (activeView == vwConfirm)
                BindConfirmView();
        }

        protected void rptDocTypeProperties_ItemDataBound(object sender, RepeaterItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
            {
                var propertyType = e.Item.DataItem as PropertyType;
                if (propertyType != null)
                {
                    var placeHolder = (Literal)e.Item.FindControl("litDropDown");
                    if (placeHolder != null)
                    {
                        var dropdown = "<select id='pt_" + propertyType.Id + "' name='pt_" + propertyType.Id + "'>";
                        dropdown += "<option value=''>Delete</option>";
                        foreach (var prop in TargetMasterDocType.PropertyTypes)
                        {
                            if (prop.DataTypeDefinition.DataType.Id == propertyType.DataTypeDefinition.DataType.Id)
                            {
                                dropdown += "<option value='"+ prop.Id +"'>"+ prop.Name +"</option>";
                            }
                        }
                        dropdown += "</select>";

                        placeHolder.Text = dropdown;
                    }
                }
            }
        }

        protected void btnSelectTargetDocType_Click(object sender, EventArgs e)
        {
            if (MasterDocType != null && MasterDocType.Id != 0 && TargetMasterDocType != null && TargetMasterDocType.Id > 0)
            {
                mvSwitchMasterDocType.SetActiveView(vwMapDocTypeProperties);
            }
            else
            {
                mvSwitchMasterDocType.SetActiveView(vwConfirm);
            }
        }

        protected void btnMapDocTypeProperties_Click(object sender, EventArgs e)
        {
            mvSwitchMasterDocType.SetActiveView(vwConfirm);
        }

        protected void btnConfirm_Click(object sender, EventArgs e)
        {
            DoSwitch();
        }

        protected void InitView()
        {
            var err = "";

            if (DocType == null || DocType.Id == 0)
                err = "Invalid Document Type ID";

            if (err != "")
            {
                lblErrorMessage.Text = err;
                mvSwitchMasterDocType.SetActiveView(vwError);
            }
            else
            {
                mvSwitchMasterDocType.SetActiveView(vwSelectTargetDocType);
            }
        }

        protected void BindSelectTargetDocTypeView()
        {
            var docTypePath = "";

            try
            {
                using (SqlConnection conn = new SqlConnection(GlobalSettings.DbDSN))
                {
                    conn.Open();

                    SqlCommand cmd = new SqlCommand(string.Format(@"DECLARE @DocTypeIds varchar(1000)

                        WITH cte_docTypes (nodeid)
                        AS
                        ( 
	                        SELECT nodeid FROM cmsContentType WHERE nodeId = {0}
	                        UNION ALL
	                        SELECT ct.nodeid FROM cmsContentType as ct
	                        JOIN cte_docTypes ON ct.masterContentType = cte_docTypes.nodeid
                        )
                        SELECT @DocTypeIds = COALESCE(@DocTypeIds + ', ', '') + CONVERT(varchar(20), cte_docTypes.nodeid)
                        FROM cte_docTypes

                        SELECT @DocTypeIds", DocType.Id), conn);

                    docTypePath = cmd.ExecuteScalar().ToString();
                }
            }
            catch 
            { }

            var docTypes = DocumentType.GetAllAsList()
                .Where(dt => dt.Id != ((MasterDocType != null) ? MasterDocType.Id : -1) && dt.Id != DocType.Id && !docTypePath.Contains(dt.Id.ToString()))
                .ToList();

            ddlTargetDocType.DataSource = docTypes;
            ddlTargetDocType.DataTextField = "Text";
            ddlTargetDocType.DataValueField = "Id";
            ddlTargetDocType.DataBind();

            if (DocType.MasterContentType > 0)
            {
                ddlTargetDocType.Items.Insert(0, new ListItem("None", ""));
            }
        }

        protected void BindMapDocTypePropertiesView()
        {
            var properties = MasterDocType.PropertyTypes;

            rptDocTypeProperties.DataSource = properties;
            rptDocTypeProperties.DataBind();
        }

        protected void BindConfirmView()
        {
            lblConfirmMessage.Text = string.Format("Change master doc type from '{0}' to '{1}'",
                (MasterDocType != null) ? MasterDocType.Text : "None",
                (TargetMasterDocType != null) ? TargetMasterDocType.Text : "None");

            if (MasterDocType != null && MasterDocType.Id != 0)
            {
                lblConfirmMessage.Text += "<br />";

                foreach (var prop in MasterDocType.PropertyTypes)
                {
                    var targetPropertyTypeId = Convert.ToInt32("0" + Request.Params["pt_" + prop.Id.ToString()]);
                    if (targetPropertyTypeId > 0)
                    {
                        var targetPropertyType = new PropertyType(targetPropertyTypeId);
                        lblConfirmMessage.Text += string.Format("<br />Convert property '{0}' to '{1}'",
                            prop.Name,
                            targetPropertyType.Name);
                    }
                    else
                    {
                        lblConfirmMessage.Text += string.Format("<br />Delete property '{0}'",
                            prop.Name);
                    }
                }
            }
        }

        protected void DoSwitch()
        {
            var sb = new StringBuilder();

            if (MasterDocType != null && MasterDocType.Id > 0)
            {
                foreach (var prop in MasterDocType.PropertyTypes)
                {
                    var targetPropertyTypeId = Convert.ToInt32("0" + Request.Params["pt_" + prop.Id.ToString()]);
                    if (targetPropertyTypeId > 0)
                    {
                        // Update property datas property type
                        // NB: Assumes you are mapping to like for like data types
                        sb.AppendFormat(@"UPDATE	cmsPropertyData
                            SET		propertytypeid = {2}
                            WHERE	id in (
		                            SELECT	pd.id
		                            FROM	cmsPropertyData as pd
				                            inner join umbracoNode as n on n.id = pd.contentNodeId
				                            inner join cmsContent as c on c.nodeId = n.id
		                            WHERE	n.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972' and
				                            c.contentType = {0} and 
				                            pd.propertytypeid = {1}
		                            )
                            
                            ",
                            DocType.Id,
                            prop.Id,
                            targetPropertyTypeId);
                    }
                    else
                    {
                        // Delete version history & property data
                        sb.AppendFormat(@"DELETE
                            FROM	cmsContentVersion
                            WHERE	id in (
		                            SELECT	cv.id
		                            FROM	cmsContentVersion cv
				                            inner join cmsPropertyData pd on pd.versionId = cv.VersionId
				                            inner join umbracoNode as n on n.id = pd.contentNodeId
				                            inner join cmsContent as c on c.nodeId = n.id
		                            WHERE	n.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972' and
				                            c.contentType = {0} and 
				                            pd.propertytypeid = {1}
		                            )

                            DELETE
                            FROM	cmsPropertyData
                            WHERE	id in (
		                            SELECT	pd.id
		                            FROM	cmsPropertyData pd
				                            inner join umbracoNode as n on n.id = pd.contentNodeId
				                            inner join cmsContent as c on c.nodeId = n.id
		                            WHERE	n.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972' and
				                            c.contentType = {0} and 
				                            pd.propertytypeid = {1}
		                            )

                            ",
                            DocType.Id,
                            prop.Id);
                    }
                }
            }

            // Swap master doc type
            sb.AppendFormat(@"UPDATE	cmsContentType
                SET		masterContentType = {1}
                WHERE	nodeId in (
			                SELECT  ct.nodeId
			                FROM	cmsContentType as ct
					                inner join cmsContent as c on c.contentType = ct.nodeId
					                inner join umbracoNode as n on n.id = c.nodeId
			                WHERE	n.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972' and
					                ct.nodeId = {0}
		                )

                ",
                DocType.Id,
                (TargetMasterDocType != null && TargetMasterDocType.Id > 0) ? TargetMasterDocType.Id : 0);

            // Execute SQL
            try
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    using (SqlConnection conn = new SqlConnection(GlobalSettings.DbDSN))
                    {
                        conn.Open();

                        SqlCommand cmd = new SqlCommand(sb.ToString(), conn);
                        cmd.ExecuteNonQuery();
                    }
                    scope.Complete();
                }
            }
            catch (TransactionAbortedException ex)
            {
                lblErrorMessage.Text = "Unable to swap master doc type.<br /><br />An error occured causing the transaction to fail:<br />" + ex.Message;
                mvSwitchMasterDocType.SetActiveView(vwError);
                return;
            }
            catch (ApplicationException ex)
            {
                lblErrorMessage.Text = "Unable to swap master doc type.<br /><br />An error occured causing the transaction to fail:<br />" + ex.Message;
                mvSwitchMasterDocType.SetActiveView(vwError);
                return;
            }

            lblSuccessMessage.Text = string.Format("Successfully swapped master doc type from '{0}' to '{1}'.",
                (MasterDocType != null) ? MasterDocType.Text : "None",
                (TargetMasterDocType != null) ? TargetMasterDocType.Text : "None");

            mvSwitchMasterDocType.SetActiveView(vwSuccess);
        }
    }
}